package com.use.utils.controller;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static java.nio.file.StandardOpenOption.*;

@RestController
public class SegmentedDownloader2 {

    private static final String DOWNLOAD_URL = "https://jfrog-patac.apps.saic-gm.com:443/artifactory/PATAC-FMS-CDN/test/cdnfiles/testMidFile2.zip";
    private static final String TEMP_DIR = "E:\\fsmFile\\temp\\";
    private static final String savePath = "E:\\fsmFile\\test.file";
    private static final int CHUNK_SIZE_MB = 200; // 每个分块200MB
    private static final int MAX_THREADS = Math.min(Runtime.getRuntime().availableProcessors() * 2, 32); // 动态线程数
    private static final int MAX_RETRIES = 3; // 最大重试次数
    private static final long RETRY_DELAY = 1000; // 重试间隔（毫秒）


    public void downloadFile() throws IOException {
        new File(TEMP_DIR).mkdirs();

        long totalSize = getFileSize();
        System.out.println("文件总大小: " + formatSize(totalSize));

        AtomicLong downloadedSize = new AtomicLong(0);
        ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);

        long chunkSize = CHUNK_SIZE_MB * 1024L * 1024L;
        long numChunks = (totalSize + chunkSize - 1) / chunkSize;

        for (int chunkId = 0; chunkId < numChunks; chunkId++) {
            long start = chunkId * chunkSize;
            long end = Math.min(start + chunkSize - 1, totalSize - 1);
            String tempFile = TEMP_DIR + "chunk-" + chunkId + ".tmp";

            File chunkFile = new File(tempFile);
            if (chunkFile.exists() && chunkFile.length() == (end - start + 1)) {
                downloadedSize.addAndGet(chunkFile.length());
                continue;
            }

            executor.execute(() -> downloadChunk(start, end, tempFile, downloadedSize, totalSize));
        }

        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.HOURS); // 等待任务完成
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("下载被中断", e);
        }

        mergeChunks(numChunks, savePath, totalSize);
        System.out.println("文件下载并合并完成");
    }

    // 获取文件总大小
    private long getFileSize() throws IOException {
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpGet headRequest = new HttpGet(DOWNLOAD_URL);
            headRequest.setHeader("Authorization", "Bearer eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJEMm9RVXZZY1lLWWVGWl9yWVgxN2dDcnVVLVRzc29DTndlSmZja2RiWG1jIn0.eyJzdWIiOiJqZmFjQDAxZ21xNXJjbjU1aDU2MDNjMWtkcjExMmthL3VzZXJzL2Ztc2Rvd25sb2FkZXIiLCJzY3AiOiJhcHBsaWVkLXBlcm1pc3Npb25zL3VzZXIiLCJhdWQiOiIqQCoiLCJpc3MiOiJqZmZlQDAwMCIsImlhdCI6MTc0Mjg4ODIwOCwianRpIjoiNzE2YzM3YzUtNTk3Zi00NWI1LWI4MzMtZmNhZjdlY2E0Y2Q2In0.aZ26wyqKZ-fLxL-cO0fFy4TTWywivN7lVYkCyo7Neg3YVxHGwkMjGbJwDI-kZ0MqMvjIDagsZbhX9SjJJ3H2Kq6uBMc-y2tmr0nWvAlfO7W-FEB6O87vWwXjEIplCgtlvbajdsZ7Rzagc-fgrSm8VxrW4UC5tgY3xQZi9KrMGwPqw9DTLW0sU8yAfSIexyMteujVGIhLYJmzXuocLIcd7aWXgqzGyFqzg_7HypSo9lorIF3EeKoMEcbiVUgynoU5iy0hU_VZSiSS-7qJhfuFQY1EGClixbSUa5mN59CZK85KizOjiRXJJseCcsLFv0DXMGbmakopevQjesrvAHj5PQ");

            try (CloseableHttpResponse response = client.execute(headRequest)) {
                return Long.parseLong(response.getFirstHeader("Content-Length").getValue());
            }
        }
    }

    // 下载分块（使用NIO优化）
    private void downloadChunk(long start, long end, String tempFile,
                               AtomicLong downloadedSize, long totalSize) {
        int retryCount = 0;
        while (retryCount <= MAX_RETRIES) {
            try (CloseableHttpClient client = HttpClients.createDefault()) {
                HttpGet request = new HttpGet(DOWNLOAD_URL);
                request.setHeader("Range", "bytes=" + start + "-" + end);
                request.setHeader("Authorization", "Bearer eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJEMm9RVXZZY1lLWWVGWl9yWVgxN2dDcnVVLVRzc29DTndlSmZja2RiWG1jIn0.eyJzdWIiOiJqZmFjQDAxZ21xNXJjbjU1aDU2MDNjMWtkcjExMmthL3VzZXJzL2Ztc2Rvd25sb2FkZXIiLCJzY3AiOiJhcHBsaWVkLXBlcm1pc3Npb25zL3VzZXIiLCJhdWQiOiIqQCoiLCJpc3MiOiJqZmZlQDAwMCIsImlhdCI6MTc0Mjg4ODIwOCwianRpIjoiNzE2YzM3YzUtNTk3Zi00NWI1LWI4MzMtZmNhZjdlY2E0Y2Q2In0.aZ26wyqKZ-fLxL-cO0fFy4TTWywivN7lVYkCyo7Neg3YVxHGwkMjGbJwDI-kZ0MqMvjIDagsZbhX9SjJJ3H2Kq6uBMc-y2tmr0nWvAlfO7W-FEB6O87vWwXjEIplCgtlvbajdsZ7Rzagc-fgrSm8VxrW4UC5tgY3xQZi9KrMGwPqw9DTLW0sU8yAfSIexyMteujVGIhLYJmzXuocLIcd7aWXgqzGyFqzg_7HypSo9lorIF3EeKoMEcbiVUgynoU5iy0hU_VZSiSS-7qJhfuFQY1EGClixbSUa5mN59CZK85KizOjiRXJJseCcsLFv0DXMGbmakopevQjesrvAHj5PQ");

                try (CloseableHttpResponse response = client.execute(request);
                     InputStream is = response.getEntity().getContent()) {

                    Path tempPath = Paths.get(tempFile);
                    try (FileChannel fosChannel = FileChannel.open(tempPath,
                            CREATE, WRITE, TRUNCATE_EXISTING)) {

                        ByteBuffer buffer = ByteBuffer.allocateDirect(20 * 1024 * 1024); // 20MB直接缓冲区
                        int bytesRead;
                        long chunkDownloadedSize = 0;

                        while ((bytesRead = is.read(buffer.array())) != -1) {
                            buffer.position(bytesRead);
                            fosChannel.write(buffer);
                            buffer.clear();
                            chunkDownloadedSize += bytesRead;
                        }

                        downloadedSize.addAndGet(chunkDownloadedSize);
                        System.out.printf("Chunk %s-%s downloaded: %d bytes\n", start, end, chunkDownloadedSize);
                        System.out.printf("总进度: %.2f%%\n", (downloadedSize.get() * 100.0) / totalSize);

                        return; // 成功后直接退出重试循环
                    }
                }
            } catch (IOException e) {
                retryCount++;
                if (retryCount > MAX_RETRIES) {
                    System.err.printf("分块 %s-%s 下载失败，重试次数耗尽%n", start, end);
                    throw new RuntimeException(e);
                }
                System.out.printf("分块 %s-%s 下载失败，剩余重试次数: %d%n", start, end, MAX_RETRIES - retryCount);
                try { Thread.sleep(RETRY_DELAY); } catch (InterruptedException ignored) {}
            }
        }
    }

    // 合并分块（使用transferTo优化）
    private void mergeChunks(long numChunks, String savePath, long expectedSize) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(savePath);
             FileChannel outChannel = fos.getChannel()) {

            for (int i = 0; i < numChunks; i++) {
                File chunk = new File(TEMP_DIR + "chunk-" + i + ".tmp");
                if (!chunk.exists()) {
                    throw new IOException("分块文件缺失: " + chunk.getName());
                }

                try (FileChannel inChannel = FileChannel.open(chunk.toPath(), READ)) {
                    inChannel.transferTo(0, inChannel.size(), outChannel); // 内核空间直接传输
                    chunk.delete(); // 合并后删除临时文件
                }
            }

            File finalFile = new File(savePath);
            if (finalFile.length() != expectedSize) {
                throw new IOException("合并后文件大小不匹配，期望: " + expectedSize + " 实际: " + finalFile.length());
            }
        }
    }

    // 格式化大小
    private String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        String pre = "KMGTPE".charAt(exp - 1) + "i";
        return String.format("%.2f %sB", bytes / Math.pow(1024, exp), pre);
    }
}
